/**
 * Copyright (C) 2009 Lazaro Brito
 *
 * This file is part of jIRC.
 * 
 * jIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * jIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with jIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.lazaro.jirc.proto;

import java.io.IOException;
import java.util.Arrays;

import org.lazaro.jirc.IRC;
import org.lazaro.jirc.irc.Channel;
import org.lazaro.jirc.irc.Target;
import org.lazaro.jirc.irc.User;
import org.lazaro.jirc.irc.Server;
import org.lazaro.jirc.misc.Utils;
import org.lazaro.jirc.msg.TopicHandler;
import org.lazaro.jirc.msg.MessageHandler;
import org.lazaro.jirc.msg.KickHandler;

/**
 * @author Lazaro Brito
 *
 */
public class IRCProtocol {
	private transient final IRC irc;

	public IRCProtocol(IRC irc) {
		this.irc = irc;
	}

	public boolean write(String message, boolean flush) {
		try {
			irc.getWriter().write(new StringBuilder().append(message).append("\r\n").toString());
			if(flush) {
				irc.getWriter().flush();
			}
			return true;
		} catch(IOException ioe) {
			irc.setConnected(false);
		}
		return false;
	}

	public boolean write(String message) {
		return write(message, true);
	}

	public void register() {
		if(irc.getDetails().getPassword() != null) {
			sendPassword();
		}
		sendNickName();
		sendUser();
	}

	public void sendPassword() {
		write(new CommandBuilder("PASS")
			.append(irc.getDetails().getPassword())
			.toString());
	}

	public void sendNickName() {
		write(new CommandBuilder("NICK")
			.append(irc.getDetails().getNickName())
			.toString());
	}

	public void sendUser() {
		write(new CommandBuilder("USER")
			.append(irc.getDetails().getName())
			.append(irc.getDetails().getName())
			.append(Utils.getExternalIP())
			.append(irc.getDetails().getName(), true)
			.toString());
	}

	public void joinChannel(Channel channel) {
		irc.getCurrentChannels().put(channel.getName(), channel);

		write(new CommandBuilder("JOIN")
			.append(channel.getName())
			.toString());

		write(new CommandBuilder("MODE")
			.append(channel.getName())
			.toString()); // checks the current mode for this

		if(irc.getCurrentTarget() == null) {
			irc.setCurrentTarget(channel);
		}
	}

	public void leaveChannel(Channel channel) {
		irc.getCurrentChannels().remove(channel.getName());
		write(new CommandBuilder("PART")
			.append(channel.getName())		
			.toString());
	}

	public void sendMessage(String message, Target target) {
		write(new CommandBuilder("PRIVMSG")
			.append(target.getName())
			.append(message, true)
			.toString());
	}

	public void sendMessage(String message) {
		sendMessage(message, irc.getCurrentTarget());
	}

	public void handle() throws Exception {
		String line = null;
		try {
			line = irc.getReader().readLine();
		} catch(Exception e) {
			irc.setConnected(false);
			throw e;
		}
		if (line != null && !line.equals("")) {
			line = line.trim();
			//System.out.println(line);

			String[] parts = line.split(" :");
			String code = parts[0];
			if(code.startsWith(":")) {
				code = code.substring(code.indexOf(" ")).trim();
			}
			if(code.contains(" ")) {
				code = code.substring(0, code.indexOf(" ")).trim();
			}
			//System.out.println("code=" + code);

			try {
				int num_code = Integer.parseInt(code);
				switch(num_code) {
				case 1:
					irc.getCurrentServers().put(parts[0].substring(1, parts[0].indexOf(" ")), new Server(parts[0].substring(1, parts[0].indexOf(" "))));
					irc.setHasRegistered(true);
					break;
				case 2:
				case 3:
				case 4:
				case 5:
				case 256:
				case 254:
				case 255:
				case 265:
				case 266:
				case 422:
				case 251:
				case 252:
				case 253:
				case 333:
				case 329:
					break;
				case 332:
					for(TopicHandler th : irc.getTopicHandlers()) {
						th.handle(irc, irc.findTarget(parts[0].substring(1, parts[0].indexOf(" "))), (Channel) irc.findTarget(parts[0].substring(parts[0].indexOf(irc.getDetails().getNickName())+irc.getDetails().getNickName().length()+1)), parts[1]);
					}
					break;
				case 353:
					Channel channel = (Channel) irc.findTarget(parts[0].substring(parts[0].indexOf("=")+2));
					if(channel != null) {
						String[] users_s = parts[1].split(" ");
						for(String user_s : users_s) {
							User user = (User) irc.findTarget(user_s);
							if(user == null) {
								user = new User(user_s);
								irc.getKnownUsers().put(user.getName(), user);
							}
							channel.getUsers().add(user);
						}
					}					
					break;
				case 366: // end of names list
					break;
				case 324:
					String args[] = parts[0].substring(parts[0].indexOf(irc.getDetails().getNickName())+irc.getDetails().getNickName().length()+1).trim().split(" ");
					Target target = irc.findTarget(args[0]);
					if(target != null) {
						target.getMode().setModes(args[1]);
					}
					break;
				default:
					System.out.println("un_ " + num_code + "=" + Arrays.toString(parts));
					break;
				}
			} catch(NumberFormatException ex) {
				switch(code.hashCode()) {
				case 2455922: // PING
					if (parts[1].length() == 8) {
						write("PONG :" + parts[1]);
					} else {
						write("PONG :" + Utils.getExternalIP());
					}
					break;
				case 403496530: // PRIVMSG
					for(MessageHandler mh : irc.getMessageHandlers()) {
						mh.handle(irc, irc.findTarget(parts[0].substring(1, parts[0].indexOf("!"))), irc.findTarget(parts[0].substring(parts[0].indexOf("PRIVMSG")+8)), parts[1]);
					}
					break;
				case 80008463: // TOPIC
					for(TopicHandler th : irc.getTopicHandlers()) {
						th.handle(irc, irc.findTarget(parts[0].substring(1, parts[0].indexOf("!"))), (Channel) irc.findTarget(parts[0].substring(parts[0].indexOf("TOPIC")+6)), parts[1]);
					}
					break;
				case -1986360616: // NOTICE
					break;
				case 2282794: // JOIN
					Channel channel = (Channel) irc.findTarget(parts[1]);
					if(channel != null) {
						User user = (User) irc.findTarget(parts[0].substring(1, parts[0].indexOf("!")));
						if(user == null) {
							user = new User(parts[0].substring(1, parts[0].indexOf("!")));
							irc.getKnownUsers().put(parts[0].substring(1, parts[0].indexOf("!")), user);
						}
						channel.getUsers().add(user);
					}
					break;
				case 2497103: // QUIT
					User user = (User) irc.findTarget(parts[0].substring(1, parts[0].indexOf("!")));
					if(user != null) {
						irc.getKnownUsers().remove(user);
						for(Channel c : irc.getCurrentChannels().values()) {
							if(c.getUsers().contains(user)) {
								c.getUsers().remove(user);
							}
						}
					}
					break;
				case 2372003: // MODE
					String name = parts[0].substring(parts[0].indexOf("MODE")+5).trim();
					String mode = null;
					if(name.contains(" ")) {
						name = name.substring(0, name.indexOf(" "));
						mode = parts[0].substring(parts[0].trim().lastIndexOf(" ")+1);
					} else {
						mode = parts[1];
					}
					Target target = irc.findTarget(name);
					if(target != null) {
						target.getMode().setModes(mode);
						System.out.println("modes for " + target + " are " + target.getMode().getPositiveModes());
					}
					break;
				case 2448371: // PART
					channel = (Channel) irc.findTarget(parts[0].substring(parts[0].indexOf("PART")+5));
					if(channel != null) {
						user = (User) irc.findTarget(parts[0].substring(1, parts[0].indexOf("!")));
						if(user != null) {
							channel.getUsers().remove(user);
						}
					}
					break;
				case 2306630: // KICK
					user = (User) irc.findTarget(parts[0].substring(1, parts[0].indexOf("!")));
					channel = (Channel) irc.findTarget(parts[0].substring(parts[0].indexOf("KICK")+5, parts[0].lastIndexOf(" ")));
					irc.getCurrentChannels().remove(channel);
					for(KickHandler kh : irc.getKickHandlers()) {
						kh.handle(irc, user, channel, parts[1]);
					}
					break;
				default:
					System.out.println("un_ " + code + "=" + Arrays.toString(parts));
					break;
				}
			}
		}
	}
}